﻿<html>
<head>
	<style type="text/css">
		.theme1-color
		{
			color: #f90;
		}
		.theme1-background
		{
			background-color: #f90;
		}
		.theme1-border
		{
			border-color: #f90 !important;
		}
		.theme2-color
		{
			color: #fc6;
		}
		.theme2-background
		{
			background-color: #fc6;
		}
		.theme2-border
		{
			border-color: #fc6 !important;
		}
		.theme3-color
		{
			color: #fff8df;
		}
		.theme3-background
		{
			background-color: #fff8df;
		}
		.theme2-border
		{
			border-color: #fff8df !important;
		}
		.contrast1-color
		{
			color: #488E00;
		}
		.contrast1-background
		{
			background-color: #488E00;
		}
		.contrast1-border
		{
			border-color: #488E00 !important;
		}
		.contrast2-color
		{
			color: #C3DE6C;
		}
		.contrast2-background
		{
			background-color: #C3DE6C;
		}
		.contrast1-border
		{
			border-color: #C3DE6C !important;
		}
		.blank-background
		{
			background-color: White;
		}
		html
		{
			font-size: 100.01%;
		}
		body, p, h1, h2, h3, h4, h5, h6, li, tr, td, th, dd, dt
		{
			font-family: Verdana,Arial,Sans-Serif;
			font-size: 10pt;
			line-height: 1.2em;
		}
		body
		{
			background-color: #fff;
			margin: 0;
		}
		h1, h2, h3, h4, h5, th
		{
			font-weight: bold;
		}
		h1
		{
			padding-bottom: 0;
			color: #488E00;
			margin-top: 10px;
			text-align: left;
			margin-bottom: 5px;
			letter-spacing: -1px;
		}
		h2, h3, h5
		{
			color: #f90;
		}
		h1
		{
			font-size: 1.6em;
		}
		h2
		{
			font-size: 13pt;
		}
		h3
		{
			font-size: 11pt;
		}
		h4
		{
			font-size: 10pt;
		}
		h5
		{
			font-size: 9pt;
			font-weight: bold;
			margin-bottom: 2px;
		}
		h6
		{
			font-size: 65%;
			color: #666;
			font-weight: normal;
		}
		acronym
		{
			cursor: help;
			border-bottom: 1px dashed #666;
		}
		pre
		{
			background-color: #FBEDBB;
			padding: 6px;
			font: 9pt "Courier New" ,Courier,mono;
			white-space: pre;
			overflow: auto !important;
			border: solid 1px #FBEDBB;
		}
		code
		{
			color: #900;
			font: 11pt "Courier New" ,Courier,mono;
		}
		kbd
		{
			background-color: #EEE;
			border-color: #CCC #AAAAAA #888 #BBBBBB;
			border-style: solid;
			border-width: 1px 3px 3px 1px;
			color: #000;
			padding: 2px 4px;
			white-space: nowrap;
		}
		table
		{
			background-color: Transparent;
		}
		img
		{
			-ms-interpolation-mode: bicubic;
		}
		dt
		{
			font-weight: bold;
			margin-top: 10px;
			margin-bottom: 6px;
		}
		dd
		{
			margin-bottom: 6px;
			margin-left: 25px;
		}
		a
		{
			text-decoration: none;
			color: #004cd5;
		}
		a:visited
		{
			color: #800080;
		}
		a:active
		{
			text-decoration: underline;
		}
		a:hover
		{
			text-decoration: underline;
		}
		a:focus
		{
			outline: none;
		}
		.container-article #content-div a[name]
		{
			color: inherit;
			text-decoration: none;
		}
		tt.equation
		{
			font-family: 'Times New Roman' ,serif;
			font-size: 10pt;
			font-style: italic;
			white-space: pre;
		}
		ul.outdent
		{
			padding-left: 15px;
			margin-left: 0;
			margin-bottom: 5px;
			margin-top: 5px;
		}
		ul.outdent li
		{
			padding-bottom: 2px;
			font-size: 8pt;
		}
		ul.download LI, LI.download
		{
			list-style-image: url(/images/download.gif);
			padding-left: .5em;
		}
		ul.compact
		{
			list-style-type: none;
			margin: 0;
			padding-left: 3px;
			line-height: 1.4em;
		}
		ul.spaced, ol.spaced
		{
			margin: 3px;
		}
		ul.spaced li, ol.spaced li
		{
			margin: 10px 0 10px 0; *margin:10px010px25px;}
		ul.tight, ol.tight
		{
			margin: 0;
			padding-left: 15px !important;
		}
		ul.delegate li, li.delegate
		{
			list-style-image: url(/images/symbols/delegate.gif);
		}
		ul.enum li, li.enum
		{
			list-style-image: url(/images/symbols/Enum.GIF);
		}
		ul.event li, li.event
		{
			list-style-image: url(/images/symbols/event.GIF);
		}
		ul.interface li, li.interface
		{
			list-style-image: url(/images/symbols/interface.GIF);
		}
		ul.literal li, li.literal
		{
			list-style-image: url(/images/symbols/literal.GIF);
		}
		ul.method li, li.method
		{
			list-style-image: url(/images/symbols/method.GIF);
		}
		ul.namespace li, li.namespace
		{
			list-style-image: url(/images/symbols/namespace.GIF);
		}
		ul.property li, li.property
		{
			list-style-image: url(/images/symbols/property.GIF);
		}
		ul.struct li, li.struct
		{
			list-style-image: url(/images/symbols/struct.GIF);
		}
		ul.compactlist li, li.compactlist
		{
			list-style-image: url(/images/smallarrow.gif);
			font-size: 8pt;
			margin-left: -1em;
			margin-bottom: 0;
		}
		ul.compactlist
		{
			margin-top: 5px;
			margin-bottom: 5px;
		}
		ul.horizontal li
		{
			display: inline;
			padding: .2em 1em;
		}
		.long-element
		{
			width: 500px;
		}
		.short-element
		{
			width: 50px;
		}
		a.button, a.button-large, .button, .button-large
		{
			color: white;
			background-color: #E08900;
			border-width: 2px;
			border-style: outset;
			font-weight: bold;
			text-decoration: none !important;
			white-space: nowrap;
			font-size: 100%;
			padding: 2px;
		}
		button.button
		{
			font-size: 90%;
		}
		.button:active
		{
			border-style: inset;
		}
		.button:hover, .button-large:hover
		{
			border-style: outset;
		}
		.button-large
		{
			font-size: 10pt !important;
			padding: 4px 9px;
		}
		div a.button.safe, div a.button.unsafe
		{
			color: white !important;
		}
		input[type=text]:focus, textarea:focus
		{
			border: 2px solid #A0ACBF;
		}
		input[type=radio]
		{
			border: none;
		}
		.small-text, .small-text li, .small-text td
		{
			font-size: 8pt !important;
		}
		.tiny-text, input-prompt, .action-link
		{
			font-size: 7pt;
		}
		.medium-text
		{
			font-size: 10pt;
		}
		.large-text
		{
			font-size: 13pt;
			font-weight: bold;
		}
		.align-left
		{
			text-align: left !important;
		}
		.align-right
		{
			text-align: right !important;
		}
		.align-center
		{
			text-align: center;
		}
		.align-justify
		{
			text-align: justify;
		}
		.align-top
		{
			vertical-align: top;
		}
		.align-middle
		{
			vertical-align: middle;
		}
		.align-bottom
		{
			vertical-align: bottom;
		}
		.float-right
		{
			float: right;
		}
		.float-left
		{
			float: left;
		}
		.float-center
		{
			margin: auto;
		}
		.top-left-fixed
		{
			position: relative;
			top: 0;
			left: 0;
		}
		.z-index-top
		{
			z-index: 10000;
		}
		.z-index-bottom
		{
			z-index: 0;
		}
		.extended
		{
			width: 100%;
		}
		.padded
		{
			padding: 10px;
		}
		.tight
		{
			margin: 0 !important;
			padding: 0 !important;
		}
		.spaced
		{
			margin: 10px;
		}
		.nowrap
		{
			white-space: nowrap;
		}
		.wrap
		{
			white-space: normal;
		}
		.fixed-layout
		{
			table-layout: fixed;
		}
		.clip-text
		{
			text-overflow: ellipsis;
		}
		.clipped
		{
			overflow: hidden;
		}
		.fluid
		{
			max-width: 10000px !important;
			width: auto !important;
		}
		.loading
		{
			background: white url(/images/animated_loading.gif) no-repeat center center;
		}
		.no-color, .no-colour, .no-color span, .no-colour span
		{
			color: black !important;
		}
		.border-none
		{
			border-style: none !important;
		}
		.border-none-left
		{
			border-left-style: none !important;
		}
		.border-none-right
		{
			border-right-style: none !important;
		}
		.border-none-sides
		{
			border-right-style: none !important;
			border-left-style: none !important;
		}
		.border-none-top
		{
			border-top-style: none !important;
		}
		.border-none-bottom
		{
			border-bottom-style: none !important;
		}
		.pre-wrap
		{
			white-space: pre-wrap;
			white-space: -moz-pre-wrap;
			white-space: -pre-wrap;
			white-space: -o-pre-wrap;
			word-wrap: break-word;
			_white-space: pre;
		}
		.header
		{
			background-color: #f90;
		}
		.toplinks
		{
			font-size: 8pt;
			color: #333;
			background-color: White;
			letter-spacing: -1px;
			padding: 2px;
		}
		.footer
		{
			margin: auto;
		}
		.row
		{
			zoom: 1;
			overflow: hidden;
		}
		.column
		{
			display: inline;
			float: left;
		}
		.column8_1
		{
			width: 12%;
			float: left;
		}
		.column8_2
		{
			width: 25%;
			float: left;
		}
		.column8_3
		{
			width: 37%;
			float: left;
		}
		.column8_4
		{
			width: 50%;
			float: left;
		}
		.column8_5
		{
			width: 62%;
			float: left;
		}
		.column8_6
		{
			width: 75%;
			float: left;
		}
		.column8_7
		{
			width: 87%;
			float: left;
		}
		.access-link
		{
			position: absolute;
			top: 0;
			left: 0;
			width: 1px;
			height: 1px;
			z-index: 100;
			border-style: none;
			overflow: hidden;
		}
		.scroll-list
		{
			overflow: auto;
			border-width: 1px;
			border-color: Gray;
			border-style: solid;
			font-size: 8pt;
			scrollbar-arrow-color: white;
			scrollbar-face-color: #f90;
			height: 150px;
		}
		.box .scroll-list
		{
			width: 120px;
			font-size: 7pt;
			white-space: nowrap;
			text-overflow: clip;
		}
		.box .scroll-list .sub-item
		{
			padding-left: 10px;
			font-size: 7pt;
			white-space: nowrap;
			text-overflow: clip;
		}
		.box
		{
			margin-bottom: 10px;
			padding: 5px;
			font-size: 8pt;
			border: solid 1px #ddd;
			border-bottom-left-radius: 5px;
			-moz-border-radius-bottomleft: 5px;
			-webkit-borderbottom-left-radius: 5px;
			border-bottom-right-radius: 5px;
			-moz-border-radius-bottomright: 5px;
			-webkit-border-bottom-right-radius: 5px; *zoom:1;_zoom:1;}
		.box.header
		{
			padding: 2px 2px 2px 5px;
			color: Black;
			font-family: Verdana,Arial,Sans-Serif;
			font-size: 13pt;
			font-weight: bold;
			background-color: #EEE;
			border: solid 1px #ddd;
			border-bottom-style: none;
			margin-bottom: 0;
			border-bottom-left-radius: 0;
			-moz-border-radius-bottomleft: 0;
			-webkit-borderbottom-left-radius: 0;
			border-bottom-right-radius: 0;
			-moz-border-radius-bottomright: 0;
			-webkit-border-bottom-right-radius: 0;
		}
		.box.header.feature
		{
			padding: 2px 2px 2px 5px;
			color: Black;
			font-family: Verdana,Arial,Sans-Serif;
			font-size: 13pt;
			font-weight: bold;
			background-color: #fff8df;
			border: solid 1px #f90;
			border-bottom-style: none;
			margin-bottom: 0;
			_text-transform: none;
			_font-family: Verdana,Arial,Sans-Serif;
			_font-size: 8pt;
			_background-color: #fff8df;
			_border: 0;
			_font-weight: normal;
		}
		.answers .box
		{
			border: 0;
		}
		.question .box
		{
			padding: 10px;
			margin: 10px 0;
			border-radius: 5px;
			-moz-border-radius: 5px;
			-webkit-border-radius: 5px;
		}
		.popup
		{
			display: none;
		}
		.raised
		{
			background-color: #fff8df;
			-moz-box-shadow: #999 5px 5px 10px 1px;
			-webkit-box-shadow: #999 5px 5px 10px 1px;
			box-shadow: #999 5px 5px 10px 1px;
		}
		table.data td
		{
			padding: 1px;
		}
		div.box, td.box
		{
			_font-size: 8pt;
			_font-weight: normal;
			_text-transform: none;
			_margin-bottom: 10px;
		}
		div.header, td.header
		{
			_background-color: #f90;
			_font-size: 13px !important;
			_font-weight: bold !important;
			_margin-bottom: 0;
			_border: 0;
		}
		div.box table.data
		{ *width:95%!important;_width:95%!important;}
		.feature
		{
			background-color: #fff8df;
			border-color: #f90;
		}
		.button-group
		{
			white-space: nowrap;
			padding-right: 1em;
			margin: 0;
		}
		.hover-container
		{
			display: block;
			position: relative;
		}
		* + html .hover-row .hover-container
		{
			position: static;
		}
		* + html .hover-row .hover-container .button-group
		{
			position: relative;
			float: right;
		}
		.hover-container .button-group, .hover-row .button-group
		{
			display: none;
		}
		.hover-container .button-group-tight, .hover-row .button-group-tight
		{
			visibility: hidden;
		}
		.hover-row.hover, .hover-row.hover td, .hover-row:hover, .hover-row:hover td
		{
			background-color: #fff8df !important;
		}
		.hover .button-group, .hover-container:hover .button-group
		{
			background-color: inherit;
			margin: 0;
			position: absolute;
			top: 0;
			right: 3px;
			display: block;
		}
		.hover-container:hover .button-group-tight
		{
			background-color: inherit;
			visibility: visible;
		}
		.cloud .hover-container:hover .button-group
		{
			top: auto;
			bottom: 0;
			right: 0;
		}
		.tag-info .button-group
		{
			top: 0;
		}
		.title-addenda
		{
			margin-top: 15px;
			float: right;
			text-align: right;
			font-size: .85em;
			font-weight: normal;
		}
		.spacer
		{
			height: 20px;
		}
		.divider
		{
			color: #f90;
			background-color: #f90;
			height: 1px;
			margin: 0;
			padding: 0;
			border: none;
		}
		.divider-faint
		{
			border-style: none;
			color: #CCC;
			background-color: #CCC;
			height: 1px;
			padding: 0;
			margin: 0;
		}
		.breadcrumb
		{
			font-size: 8pt;
		}
		.toolbar
		{
			border: solid 1px transparent;
			_border: solid 1px white;
			padding: 1px;
			-webkit-transition: border-color .1s linear;
			-moz-transition: border-color .1s linear;
			-o-transition: border-color .1s linear;
			transition: border-color .1s ease-in;
			-webkit-transition: background-color .1s linear;
			-moz-transition: background-color .1s linear;
			-o-transition: background-color .1s linear;
			transition: background-color .1s linear;
		}
		.toolbar:hover
		{
			border: solid 1px #f90;
			background-color: #fc9;
			-webkit-transition: border-color .1s linear;
			-moz-transition: border-color .1s linear;
			-o-transition: border-color .1s linear;
			transition: border-color .1s ease-in;
			-webkit-transition: background-color .1s linear;
			-moz-transition: background-color .1s linear;
			-o-transition: background-color .1s linear;
			transition: background-color .1s linear;
		}
		.active .toolbar
		{
			border: 1px solid #fc6;
		}
		img.toolbar
		{
			padding: 2px;
			border: solid 1px transparent;
			vertical-align: bottom;
		}
		img:hover.toolbar
		{
			border: solid 1px #fff8df !important;
		}
		button.toolbar
		{
			border: solid 1px white;
			background-color: white;
			padding: 0;
			margin: 0;
			padding: 1px;
		}
		.list-socialbookmark
		{
			position: absolute;
			top: -90px;
			left: -5000px;
			z-index: 200;
			display: none;
			white-space: nowrap;
			padding: 10px;
			border: 2px solid #f90;
			background-color: #ffc;
			border-radius: 5px;
			-moz-border-radius: 5px;
			-webkit-border-radius: 5px;
			text-align: left;
		}
		.search-bar
		{
			padding: 2px 5px;
		}
		.admin-bar
		{
			margin: 0 20px;
		}
		.search-bar input.search
		{
			background: white url(/Images/search.gif) no-repeat right;
			width: 225px;
		}
		.search-bar .search-advanced
		{
			padding: 8px;
			width: 205px;
			z-index: 1000;
			background-color: #fff8df;
			border: solid 1px #ccc;
			box-shadow: 2px 2px 7px rgba(0,0,0,0.5);
			-moz-box-shadow: 2px 2px 7px rgba(0,0,0,0.5);
			-webkit-box-shadow: 2px 2px 7px rgba(0,0,0,0.5);
			position: absolute;
			top: 1px;
			right: 5px;
		}
		.search-bar:hover .popup
		{
			display: block;
		}
		.search-highlight
		{
			border-bottom: 1px dotted #f90;
			font-weight: bold;
		}
		.search-box
		{
			padding: 20px 0;
			font-weight: bold;
			white-space: nowrap;
			font-size: 14px;
		}
		.search-box input
		{
			font-size: 18px;
		}
		.filter-options
		{
			background-color: transparent;
			padding: 10px 0;
		}
		.filter-pane
		{
			width: 160px;
			float: left;
			margin-left: -5px;
			background-color: white;
		}
		.filter-pane .menu1
		{
			padding: 2px 2px 3px 0;
			font-size: 11pt;
			font-weight: bold;
			margin: 0;
			letter-spacing: 0;
			background-color: transparent;
			color: Black;
			margin-bottom: 5px;
			border: 0;
		}
		.filter-pane .menu1 a
		{
			color: #004cd5 !important;
		}
		.filter-pane .menu2
		{
			background-color: transparent;
			border: none;
			margin-bottom: 10px;
			font-size: 9pt;
			font-family: Verdana,arial,Sans-Serif;
			font-weight: normal;
		}
		.filter-pane .menu2 label, .filter-pane .menu2 a
		{
			color: #004cd5;
		}
		.filter-pane .options
		{
			border-top: 1px solid #f90;
			border-bottom: 1px solid #f90;
		}
		.filter-pane .options label
		{
			color: Black;
		}
		.filter-pane .advanced
		{
			padding-left: 0;
		}
	</style>
</head>
<body>
	<pre>
Title:       Composite Primary Keys
Author:      Type Your Code Project User Name
Email:       Type Code Project E-mail Login
Member ID:   12345 (seen from Settings, in URL "mid=12345")
Language:    Type Languages that Apply to Your Article (C# 3.0, etc.)
Platform:    Type Platforms that Apply to Your Article (Windows, etc.)
Technology:  NHibernate, .NET
Level:       Intermediate
Description: A library and some ideas to work with localizable data on NHibernate
Section      Type the Code Project Section you Wish the Article to Appear
SubSection   Type the Code Project SubSection you Wish the Article to Appear
License:     CPOL (default - select your desired license from our list)</pre>
	<h2>
		Introduction
	</h2>
	<p>
		Primary Keys are very usually autoincrementing integers or Guid. Unfortunatly, many
		domains need much more than that. Primary keys are not defined as single column
		but just as unique. Sooner rather than later you&#39;d find your self in the need
		for composite primary keys. When using composite keys, it is very usefull being able
		to apply equality functions to key values. Actually, nhibernate requires 
		composite keys to override equality members (Equals and GetHashCode). This article is about making the proccess
		of implementing equality funcions painless.
	</p>
	<h2>
		Contents</h2>
	<ol>
		<li><a href="#TheModel">The Model</a></li>
		<li><a href="#AutomaticEqualityFunctions">Automatic Equality Functions</a></li>
		<li><a href="#UsingTheCode">Using the Code</a></li>
		<li><a href="#Statistics">Statistics</a></li>
		<li><a href="#Conclusions">Conclusions</a></li>
		<li><a href="#HelpfullLinks">Helpfull Links</a></li>
	</ol>
	<a id="TheModel"></a>
	<h2>
		The Model
	</h2>
	<p>
		Let&#39;s take a look at the following Domain.
	</p>
	<pre lang="XML">
&lt;hibernate-mapping xmlns=&quot;urn:nhibernate-mapping-2.2&quot; assembly=&quot;Myotragus.Data.Tupples.Tests&quot; namespace=&quot;Myotragus.Data.Tupples.Tests.Domain&quot;&gt; 
	
	&lt;class name=&quot;Product&quot;&gt; 
		&lt;id name=&quot;Id&quot; column=&quot;ProductId&quot;&gt;
			&lt;generator class=&quot;identity&quot;/&gt;
		&lt;/id&gt;
		&lt;property name=&quot;Name&quot;/&gt; 
	&lt;/class&gt; 
	
	&lt;class name=&quot;Category&quot;&gt; 
		&lt;id name=&quot;Id&quot; column=&quot;CategoryId&quot;&gt; 
			&lt;generator class=&quot;identity&quot;/&gt; 
		&lt;/id&gt; 
		&lt;property name=&quot;Name&quot;/&gt; 
	&lt;/class&gt;

	&lt;class name=&quot;CategoryProducts&quot;&gt; 
		&lt;composite-id name=&quot;Key&quot;&gt; 
			&lt;key-property name=&quot;ProductId&quot;/&gt; 
			&lt;key-property name=&quot;CategoryId&quot;/&gt; 
		&lt;/composite-id&gt;
		&lt;property name=&quot;CustomDescription&quot;/&gt;
	&lt;/class&gt;
	
&lt;/hibernate-mapping&gt;</pre>
	<p>
		<a id="Excerpt1"></a>Code Excerpt 1: NHibernate mapping for sample Domain.
	</p>
	<p>
		In previous code a 3 entities Domain is defined. Product and Category need no explanation.
		CategoryProducts, in the other hand, does. If you are experienced with NHibernate,
		you&#39;d probably use a collection within both Product and Category to represent
		many-to-many relationships. I prefer to leave my POCOs clear of relationships, but
		that&#39;s me. For the sake of this example, we&#39;re going to use this mapping 
		the way I would do it in real life.
		Let&#39;s now take a look at the POCOs.</p>
	<pre lang="CS">
    	public class Category 
	{ 
		public virtual int Id { get; set; } 
		public virtual string Name { get; set; } 
	}
  
	public class Product 
	{ 
		public virtual int Id { get; set; } 
		public virtual string Name { get; set; } 
	}  </pre>
	<p>
		<a id="Excerpt2"></a>Code Excerpt 2: Simple POCOs. Product and Category
	</p>
	<pre lang="CS">
    	public class CategoryProducts 
	{ 
		public CategoryProducts() 
		{ 
			Key = new CategoryProductsKey() ; 
		} 

		public virtual CategoryProductsKey Key { get; set; } 
		public virtual int ProductId 
		{ 
			get { return Key.ProductId ;} 
			set { Key.ProductId = value ; } 
		} 
		public virtual int CategoryId 
		{ 
			get { return Key.CategoryId;} 
			set { Key.CategoryId = value ;} 
		} 
		public virtual string CustomDescription { get;set;} 
	}</pre>
	<p>
		<a id="Excerpt3"></a>Code Excerpt 3: No so simple POCO.
	</p>
	<p>
		CategoryProducts uses a composite key with 2 fields, one references Product the
		other, Category. NHibernate forces to override equality member in composite keys
		types. Now let&#39;s take a look at the key implementation.</p>
	<pre lang="CS">
        	public class CategoryProductsKey
	{	
		public int ProductId { get; set; }
		public int CategoryId { get; set; }

		public override in GetHashCode()
		{
			return ProductId ^ CategoryId ;
		}

		public override Equals(object x)
		{
			return Equals(x as CategoryProductsKey) ;
		}
		public bool Equals(CategoryProductsKey x)
		{
			return x != null &amp;&amp; x.ProductId == ProductId &amp;&amp;
				x.CategoryId == CategoryId ;
		}
	}    </pre>
	<p>
		<a id="Excerpt4"></a>Code Excerpt 4: Composite key implementation.
	</p>
	<p>
		As you can see, equality members implementation has been very simple. Actually is
		extremely straight forward in most cases.
	</p>
	<a id="AutomaticEqualityFunctions"></a>
	<h2>
		Automatic Equality Functions
	</h2>
	<p>
		Let&#39;s now formally (C#) define a straightforward equality implementation for
		composite key.
	</p>
	<pre lang="CS"> 	public bool AreEqual(TKey x, TKey)
	{
		var result = true ;

	 	foreach(var property in typeof(TKey).GetProperties(All))
			result &amp;= object.Equals(property.GetValue(x), property.GetValue(y));

		return result ;
	}</pre>
	<p>
		Code Excerpt 5: Straightforward Equals implementation.
	</p>
	<p>
		A couple of optimizations could be done, but right now they are not important.</p>
	<pre lang="CS">&nbsp;	public in GetHashCode(TKey x)
	{
		var getHashCodeMethod = typeof(object).GetMethod(&quot;GetHashCode&quot;) ;
		var result = 0;

		foreach(var property in typeof(TKey).GetProperties(All))
			return ^= getHashCodeMethod(property.GetValue(x));

		return result ;
	}&nbsp;</pre>
	<p>
		Code Excerpt 6: Straightforward GetHashCode implementation.</p>
	<a id="UsingTheCode"></a>
	<h2>
		Using the code
	</h2>
	<p>
		Got the idea, right?? What we&#39;re gonna do now is to encapsulate this definition
		in a class. This class would generate equality functions, clients later would be
		able to use to compare composite keys. Using it would look just like:</p>
	<pre lang="CS">&nbsp;	var o1 = new TKey { P1 = v11, P2 = v21, P3 = v31 } ;
	var o2 = new TKey { P1 = v21, P2 = v22, P3 = v33 } ;

	Func&lt;TKey, TKey, bool&gt; AreEquals = EqualityFunctionsGenerator&lt;TKey&gt;.CreateEqualityComparer();
	
	var r = AreEquals(o1, o2) ; // would work as expected

	Func&lt;TKey, int&gt; GetHashCode = EqualityFunctionsGenerator&lt;TKey&gt;.CreateGetHashCode();

	var c1 = GetHashCode(o1) ;
	var c2 = GetHashCode(o2) ;</pre>
	<p>
		Code Excerpt 7: Using automatically generated funcions.</p>
	<p>
		You could think of thousand of uses for it, but actually the best use you could
		give to this functions would be overriding object&#39;s definition and letting clients
		do the rest.</p>
	<pre lang="CS"> 	public class Tupple&lt;TObject&gt; : IEquatable&lt;TObject&gt; 
		where TObject : class 
	{ 
		private static readonly Func&lt;TObject, int&gt; GetHashCodeMethod = EqualityFunctionsGenerator&lt;TObject&gt; .CreateGetHashCode(); 
		private static readonly Func&lt;TObject, TObject, bool&gt; EqualsMethod = EqualityFunctionsGenerator&lt;TObject&gt; .CreateEqualityComparer(); 

		public override bool Equals(object obj) 
		{ 
			return Equals(obj as TObject); 
		} 

		public override int GetHashCode() 
		{ 
			var @this = ((object)this) as TObject; 
			if (@this == null) return 0 ; 
			return GetHashCodeMethod(@this); 
		} 

		public bool Equals(TObject other) 
		{ 
			var @this = ((object)this) as TObject ; 
			if (other == null || @this == null) return false ; 
			return EqualsMethod(@this, other); 
		} 
	}</pre>
	<p>
		Code Excerpt 8: Implementation of Tupple.</p>
	<p>
		Extending tupple would make all work.</p>
	<pre lang="CS">&nbsp;	public class CategoryProductsKey : Tupple&lt;CategoryProductsKey&gt;
	{
		public virtual int ProductId { get; set; }
		public virtual int CategoryId { get; set; }
	}&nbsp;</pre>
	<p>
		Code Excerpt 9: Wow!! That was easy.</p>
	<p>
		Just missing the whole implementation for the functions generator, here it is:</p>
	<pre lang="CS">
	public class EqualityFunctionsGenerator&lt;TObject&gt;
	{
		public static readonly Type TypeOfTObject = typeof(TObject);
		public static readonly Type TypeOfBool = typeof(bool);
		public static readonly MethodInfo MethodEquals = typeof(object).GetMethod("Equals", 
			BindingFlags.Static | BindingFlags.Public);
		public static readonly MethodInfo MethodGetHashCode = typeof(object).GetMethod("GetHashCode",
			BindingFlags.Instance | BindingFlags.Public);

		public static Func&lt;TObject, TObject, bool&gt; CreateEqualityComparer()
		{
			var x = Expression.Parameter(TypeOfTObject, "x");
			var y = Expression.Parameter(TypeOfTObject, "y");
			
			var result = (Expression)Expression.Constant(true, TypeOfBool);

			foreach (var property in GetProperties())
			{
				var comparison = CreatePropertyComparison(property, x, y);
				result = Expression.AndAlso(result, comparison);
			}

			return Expression.Lambda&lt;Func&lt;TObject, TObject, bool&gt;&gt;(result, x, y).Compile();
		}

		private static Expression CreatePropertyComparison(PropertyInfo property, Expression x, Expression y)
		{
			var type = property.PropertyType;

			var propertyOfX = GetPropertyValue(x, property);
			var propertyOfY = GetPropertyValue(y, property);

			return (type.IsValueType)? CreateValueTypeComparison(propertyOfX, propertyOfY) 
				:CreateReferenceTypeComparison(propertyOfX, propertyOfY);
		}

		private static Expression GetPropertyValue(Expression obj, PropertyInfo property)
		{
			return Expression.Property(obj, property);
		}

		private static Expression CreateReferenceTypeComparison(Expression x, Expression y)
		{
			return Expression.Call(MethodEquals, x, y);
		}

		private static Expression CreateValueTypeComparison(Expression x, Expression y)
		{
			return Expression.Equal(x, y);
		}		

		public static IEnumerable&lt;PropertyInfo&gt; GetProperties()
		{
			return TypeOfTObject.GetProperties(BindingFlags.Instance | BindingFlags.Public);
		}

		public static Func&lt;TObject, int&gt; CreateGetHashCode()
		{
			var obj = Expression.Parameter(TypeOfTObject, "obj");
			var result = (Expression)Expression.Constant(0);

			foreach (var property in GetProperties())
			{
				var hash = CreatePropertyGetHashCode(obj, property);
				result = Expression.ExclusiveOr(result, hash);
			}

			return Expression.Lambda&lt;Func&lt;TObject, int&gt;(result, obj).Compile();
		}

		private static Expression CreatePropertyGetHashCode(Expression obj, PropertyInfo property)
		{
			var type = property.PropertyType;

			var propertyOfObj = GetPropertyValue(obj, property);

			return type.IsValueType ? CreateValueTypeGetHashCode(propertyOfObj) 
				: CreateReferenceTypeGetHashCode(propertyOfObj);
		}

		private static Expression CreateReferenceTypeGetHashCode(Expression value)
		{
			return Expression.Condition(
				Expression.Equal(Expression.Constant(null), value),
				Expression.Constant(0),
				Expression.Call(value, MethodGetHashCode));
		}

		private static Expression CreateValueTypeGetHashCode(Expression value)
		{
			return Expression.Call(value, MethodGetHashCode);
		}

		private static Expression CheckForNull(Expression value)
		{
			return Expression.Condition(
				Expression.Equal(Expression.Constant(null), value),
				Expression.Constant(0),
				value);
		}
	}</pre>
	<p>
		Code Excerpt 10: Equality functions generator
	</p>
	<a id="Statistics"></a>
	<h2>
		Statistics
	</h2>
	<p>
		What is it good for if it is slow? I ran some performance test, results were not
		as good as I expected, but they were good enough. Test were run on a 1 CPU/1GB
		VirtualBox virtual machine, over a Phenom II x6 1055T 2.46Ghz. 2 tupple types were
		created to perform the tests upon, an automatic and a manually implemented. Performace
		is shown along with equivalent test over int, Point and a manually implemented tupple.
	</p>
	<pre lang="CS"> 	public class AutomaticCompositeKey : Tupple&lt;AutomaticCompositeKey&gt; 
	{ 
		public string KeyField1 { get; set; } 
		public int KeyField2 { get; set; } 
		public int KeyField3 { get; set; } 
	}

	public class ImplementedCompositeKey
	{
		public string KeyField1 { get; set; } 
		public int KeyField2 { get; set; } 
		public int KeyField3 { get; set; } 

		public override int GetHashCode {...}
		public override bool Equals(object x) {...}
	}</pre>
	<p>
		Code Excerpt 11: Tupple made for testing</p>
	<h3>
		Equals Test Results</h3>
	<table>
		<tr style="background-color: #9BBB59; color: White; font-weight: bold;">
			<td>
				Test
			</td>
			<td>
				10M Cases
			</td>
			<td>
				100M Cases
			</td>
			<td>
				1T Cases
			</td>
		</tr>
		<tr style="background-color: #CDDDAC;">
			<td style="background-color: #9BBB59; color: White; font-weight: bold;">
				ints equality test
			</td>
			<td>
				0
			</td>
			<td>
				0.04
			</td>
			<td>
				1.747
			</td>
		</tr>
		<tr style="background-color: #E6EED5;">
			<td style="background-color: #9BBB59; color: White; font-weight: bold;">
				Point equality using == operator
			</td>
			<td>
				0.01
			</td>
			<td>
				0.1
			</td>
			<td>
				1.398
			</td>
		</tr>
		<tr style="background-color: #CDDDAC;">
			<td style="background-color: #9BBB59; color: White; font-weight: bold;">
				Point equality using Equals
			</td>
			<td>
				0.08
			</td>
			<td>
				0.671
			</td>
			<td>
				11.236
			</td>
		</tr>
		<tr style="background-color: #E6EED5;">
			<td style="background-color: #9BBB59; color: White; font-weight: bold;">
				Manually implemented tupple
			</td>
			<td>
				0.06
			</td>
			<td>
				0.491
			</td>
			<td>
				18.448
			</td>
		</tr>
		<tr style="background-color: #CDDDAC;">
			<td style="background-color: #9BBB59; color: White; font-weight: bold;">
				Automatic tupple
			</td>
			<td>
				0.19
			</td>
			<td>
				1.122
			</td>
			<td>
				33.076
			</td>
		</tr>
	</table>
	<h3>
		GetHashCode Test Results</h3>
	<table>
		<tr style="background-color: #9BBB59; color: White; font-weight: bold;">
			<td>
				Test
			</td>
			<td>
				10M Cases
			</td>
			<td>
				1M Cases
			</td>
			<td>
				1T Cases
			</td>
		</tr>
		<tr style="background-color: #CDDDAC;">
			<td style="background-color: #9BBB59; color: White; font-weight: bold;">
				Point
			</td>
			<td>
				0.01
			</td>
			<td>
				0.05
			</td>
			<td>
				28.521
			</td>
		</tr>
		<tr style="background-color: #E6EED5;">
			<td style="background-color: #9BBB59; color: White; font-weight: bold;">
				Manually implemented tupple
			</td>
			<td>
				0.09
			</td>
			<td>
				0.831
			</td>
			<td>
				9.136
			</td>
		</tr>
		<tr style="background-color: #CDDDAC;">
			<td style="background-color: #9BBB59; color: White; font-weight: bold;">
				Automatic tupple
			</td>
			<td>
				0.23
			</td>
			<td>
				1.402
			</td>
			<td>
				0.496
			</td>
		</tr>
	</table>
	<p>
		Some additional cases were run over the automatic tupple to determine executions
		per second. An excel with a linear regression to determine following values is 
		also available.</p>
	<table>
		<tr style="background-color: #9BBB59; color: White; font-weight: bold;">
			<td>
				Function
			</td>
			<td>
				Million Times per second
			</td>
		</tr>
		<tr style="background-color: #CDDDAC;">
			<td style="background-color: #9BBB59; color: White; font-weight: bold;">
				Equals
			</td>
			<td>
				85.4
			</td>
		</tr>
		<tr style="background-color: #E6EED5;">
			<td style="background-color: #9BBB59; color: White; font-weight: bold;">
				GetHashCode
			</td>
			<td>
				67.53
			</td>
		</tr>
	</table>
	
	<a id="Conclusions"></a>
	<h2>
		Conclusions
	</h2>
	<p>
		Everytime I found a repetitive task I try to make automatic. Reflection, Emit 
		and now Linq.Expressions are amaizingly helpfull for doing so. This small 
		package is part of a library I&#39;m finishing these days. Hope you&#39;d find this 
		usefull.</p>
		<a id="UsefullLinks"></a>
	<h2>
		Usefull links</h2>
	<ul>
		<li><a href="http://www.nhforge.org/doc/nh/en/index.htmlNHibernate">Reference Documentation</a></li>
		<li>
			<a href="http://nhforge.org/blogs/nhibernate/archive/2010/06/30/nhibernate-and-composite-keys.aspx">
			NHibernate Composite Keys</a></li>
		<li>
			<a href="http://code.google.com/p/nlipsum/">NLipsum, great random strings 
			generator.</a></li>
	</ul>
</body>
</html>
